Skip to main content

How Do I Configure Java RESTful Engine for Printing?

info

This article describes how to configure the Java RESTful Engine to send output directly to a printer.

Manual Windows Installation

If you performed a manual installation of the Java RESTful Engine with Tomcat on Windows from here, this should work out of the box and no additional configuration is necessary!

Docker Installation

If you set up the Java RESTful Engine through Docker, there are a few steps you need to take to allow the Java RESTful Engine to send jobs to your printers.

Prerequisites (Windows Shared Printers Only)

If you are connecting to a printer shared from a Windows machine, you must complete these steps on the Windows host before configuring Docker. If you are not using a Windows Shared Printer, you can skip this section.

1. Create a local Windows user account for printer authentication

Azure AD and Microsoft accounts do not support the SMB authentication used by CUPS. You must create a dedicated local account.

  • Go to Settings > Accounts > Other users > Add other user
  • Select I don't have this person's sign-in info, then Add a user without a Microsoft account
  • Set a username and password (e.g. printuser)

2. Share the printer

  • Go to Settings > Bluetooth & devices > Printers & scanners > [Your Printer] > Printer properties > Sharing tab
  • Check Share this printer
  • On the Security tab, grant Print permission to the local account you just created

3. Find the exact share name

The SMB share name may differ from the printer's display name. Find it by running the following in a Windows Command Prompt:

net share

Note the share name listed next to your printer — you will need it when adding the printer to CUPS.


Step 1: Exec Into the Container

docker exec -it <container_name> bash

Step 2: Install Required Packages

apt update && apt install -y \
cups cups-client cups-bsd cups-filters \
smbclient \
xvfb libxtst6 libxi6 libxrender1 libxt6 libxext6 libx11-6
PackagesPurpose
cups cups-client cups-bsd cups-filtersCUPS printing system — required for Java to discover printers
smbclientSMB backend for CUPS — required for Windows shared printers
xvfbVirtual framebuffer — provides a fake display since containers have no monitor
libxtst6 libxi6 libxrender1 libxt6 libxext6 libx11-6X11 native libraries — required by Java AWT in non-headless mode

Step 3: Start CUPS and Add Printers

Start the CUPS service:

cupsd

Then add your printer. The command varies depending on printer type.

IPP network printer:

lpadmin -p <PrinterName> -E -v ipp://PRINTER_IP:631/ipp/print -m everywhere
tip

If you get a Host is down error on port 631, your printer may use port 80 instead. Try:

lpadmin -p <PrinterName> -E -v ipp://PRINTER_IP/ipp/print -m everywhere

Or over HTTPS (port 443):

lpadmin -p <PrinterName> -E -v ipps://PRINTER_IP/ipp/print -m everywhere

The exact IPP path (/ipp/print, /ipp, etc.) can be found in your printer's web interface at http://PRINTER_IP.

Raw TCP/IP printer:

lpadmin -p <PrinterName> -E -v socket://PRINTER_IP:9100 -m raw

Windows shared printer (SMB):

lpadmin -p <PrinterName> -E -v 'smb://<localuser>:<password>@host.docker.internal/<WindowsShareName>' -m raw
How to Identify Your Printer Type
SituationPrinter Typelpadmin URI format
Printer is plugged into a Windows PC and sharedWindows Shared (SMB)smb://<user>:<password>@<host>/<ShareName>
Printer has its own IP and a web interface at http://PRINTER_IPIPP Network Printeripp://PRINTER_IP:631/ipp/print
Printer has its own IP, no web interface, or is a label/industrial printerRaw TCP/IPsocket://PRINTER_IP:9100
You connect on Windows via \\PC\PrinterNameWindows Shared (SMB)smb://<user>:<password>@<host>/<ShareName>

You can also test connectivity from inside the container to confirm:

  • SMB: smbclient -L <hostname> -U <user>
  • IPP: curl http://PRINTER_IP:631/
  • Raw TCP/IP: nc -zv PRINTER_IP 9100
tip

Replace <PrinterName> with the name you will use in the MainPrinter field of your POST request.

<WindowsShareName> is the share name from net share, which may differ from the printer's display name. You can also list all available shares from inside the container with:

smbclient -L host.docker.internal -U <localuser>

If your password contains special characters such as !, $, or &, wrap the entire -v value in single quotes to prevent shell interpretation.

Verify the printer was added successfully:

lpstat -p -d

Step 4: Configure Tomcat for Non-Headless Mode

Java's print API requires access to a display. You must disable headless mode and set the display variable so Tomcat picks them up at startup:

cat > /usr/local/tomcat/bin/setenv.sh << 'EOF'
export JAVA_OPTS="$JAVA_OPTS -Djava.awt.headless=false"
export DISPLAY=:0
EOF

chmod +x /usr/local/tomcat/bin/setenv.sh

Step 5: Commit the Container and Restart With the Correct Startup Order

Since Xvfb must be running before Tomcat, you need to commit the container (to preserve all installed packages and configuration) and recreate it with the correct startup command.

Exit the container first:

exit

Then from the host:

docker stop <container_name>
docker commit <container_name> <image_name>:printing
docker rm <container_name>

docker run -d \
--name <container_name> \
-p 8080:8080 \
<image_name>:printing \
bash -c "cupsd && Xvfb :0 -screen 0 1024x768x24 & sleep 1 && catalina.sh run"

This ensures CUPS and Xvfb are always started before Tomcat each time the container runs.

caution

CUPS and Xvfb must be running before Tomcat starts. Java permanently caches the display state on first use — if Tomcat starts without a display available, print requests will fail for the lifetime of that container process even if Xvfb is started afterwards. Follow the steps below carefully to ensure the correct startup order.

tip

<image_name> can be anything you would like the image name to be. You can also omit the "printing" tag if you'd like.

note

Because the startup command is now baked into the docker run command, CUPS and Xvfb will start automatically on every container restart — no manual intervention required.

Step 6: Test

Send a POST request with OutputFormat set to prn and MainPrinter set to the printer name used in the lpadmin -p command:

{
"ConnectionString": "{TEMPLATE_URL}",
"OutputFormat": "prn",
"Format": "pdf",
"MainPrinter": "{PRINTER_NAME}"
}

Troubleshooting

ErrorCauseFix
No local or network printers foundjava.awt.headless=true (default) prevents Java from querying CUPSEnsure setenv.sh has -Djava.awt.headless=false and restart the container
Can't connect to X11 window serverXvfb was not running before Tomcat startedFull container restart — use the startup command in Step 5
Could not initialize class GraphicsEnvironment$LocalGEAn earlier request failed before Xvfb was ready; the JVM cached the failureFull container restart required
libXtst.so.6: cannot open shared object fileMissing X11 librariesInstall libxtst6 libxi6 libxrender1 libxt6 libxext6 libx11-6
Bad device-uri scheme "smb"SMB backend not installedInstall smbclient
NT_STATUS_LOGON_FAILUREAzure AD or Microsoft account used for SMB authCreate a local Windows account instead
NT_STATUS_BAD_NETWORK_NAMEWrong share name in the URIRun smbclient -L host.docker.internal -U <user> to find the correct share name
Special characters in password failBash interprets !, $, etc. as shell syntaxWrap the -v URI value in single quotes
Address already in use when starting TomcatTomcat is already runningDo not run catalina.sh run manually — use the commit/restart approach in Step 5